define([
    'js/resolve',
    'highlight',
    'summernote.zh-CN',
    'js/components/util',
    'js/components/combobox',
    'js/components/dialog',
    'js/components/colorpicker',
    'js/components/tablepicker',
    'js/components/headerlist',
    'js/components/hyperlink',
    'js/components/imageupload',
    'js/components/sourcecode',
    'js/components/prettier'
], function(resolve) {
    angular.module('app.summernote', [
        'app.util',
        'app.combobox',
        'app.dialog',
        'app.colorpicker',
        'app.tablepicker',
        'app.headerlist',
        'app.hyperlink',
        'app.imageupload',
        'app.sourcecode',
        'app.prettier'
    ])
    .directive('summernote', summernoteDirective)
    .run(function($templateCache) {
        $templateCache.put('templates/extendbar/summernote.html',
            '<div class="btn-group-vertical">' +
            '  <colorpicker icon="\'fa-font\'" forecolor="\'rgb(51,51,51)\'" backcolor="\'rgb(255,255,0)\'" color-tips="\'字体颜色\'"' +
            '      position="\'right\'" on-selected="extendbar.fontColor.click"></colorpicker>' +
            '  <div class="btn-group" title="字体大小">' +
            '    <combobox vertical="true" options="extendbar.fontSize.options"' +
            '        on-selected="extendbar.fontSize.click"></combobox>' +
            '  </div>' +
            '  <div class="btn-group" title="行高">' +
            '    <combobox vertical="true" options="extendbar.lineHeight.options"' +
            '        on-selected="extendbar.lineHeight.click"></combobox>' +
            '  </div>' +
            '  <headerlist position="\'right\'" on-selected="extendbar.header.click"></headerlist>' +
            '  <button class="btn btn-default" title="无序列表" ng-click="extendbar.unorder.click()">' +
            '      <i class="fa fa-list-ul"></i></button>' +
            '  <button class="btn btn-default" title="有序列表" ng-click="extendbar.order.click()">' +
            '      <i class="fa fa-list-ol"></i></button>' +
            '  <hyperlink on-selected="extendbar.link.click"></hyperlink>' +
            '  <imageupload on-selected="extendbar.image.click"></imageupload>' +
            '  <tablepicker position="\'right\'" on-selected="extendbar.table.click"></tablepicker>' +
            '  <sourcecode position="\'right\'" on-selected="extendbar.source.click"></sourcecode>' +
            '</div>');
    });

    function summernoteDirective($rootScope, $compile, $timeout, $templateCache, _util, _dialog, _prettier) {
        return resolve({
            restrict: 'E',
            replace: true,
            templateUrl: 'js/templates/summernote.html',
            scope: {
                file: '=',
                context: '=',
                onChanged: '<'
            },
            compile: function() {
                var toolscope = $rootScope.$new(true);
                toolscope.extendbar = {
                    'fontColor': { click: null },
                    'fontSize': { options: _.map([8, 9, 10, 11, 12, 14, 16, 20], function(size) {
                        return {
                            value: size + 'px',
                            label: size + 'px',
                            style: { 'font-size': size + 'px' },
                            selected: size == 16
                        };
                    }), click: null },
                    'lineHeight': { options: _.map(['0.5', '0.6', '0.8', '1.0', '1.2', '1.4', '1.5', '2.0'],
                            function(height) {
                                return {
                                    value: height,
                                    label: height,
                                    selected: height == '1.5'
                                };
                            }),
                        click: null
                    },
                    'header': { click: null },
                    'unorder': { click: null },
                    'order': { click: null },
                    'link': { click: null },
                    'image': { click: null },
                    'table': { click: null },
                    'source': { click: null }
                };
                var template = $templateCache.get('templates/extendbar/summernote.html');
                var extendbar = $($compile(template)(toolscope));
                extendbar.css('display', 'none');
                extendbar.appendTo($('.extendbar'));

                // summernote dom
                angular.extend($.summernote.dom, {
                    isOnPre: function(range) {
                        var dom = $.summernote.dom;
                        var ancestor = dom.ancestor(range.sc, dom.isPre);
                        return !!ancestor && ancestor === dom.ancestor(range.ec, dom.isPre);
                    }
                });
                // summernote options-buttons
                angular.extend($.summernote.options.buttons, {
                    codeDialogShow: function(context) {
                        return $.summernote.ui.button({
                            contents: '<i class="note-icon-code"></i>',
                            tooltip: '编辑代码',
                            click: context.createInvokeHandler('codeDialog.show')
                        }).render();
                    },
                    codeDelete: function(context) {
                        return $.summernote.ui.button({
                            contents: '<i class="note-icon-trash"></i>',
                            tooltip: '删除代码',
                            click: function() {
                                const dom = $.summernote.dom;
                                const range = context.invoke('editor.getLastRange');
                                if (dom.isOnPre(range)) {
                                    var pre = dom.ancestor(range.sc, dom.isPre);
                                    newRange = $.summernote.range.createFromNode(pre);
                                    newRange.select();
                                    context.invoke('editor.setLastRange');
                                    context.invoke('editor.beforeCommand');
                                    newRange.deleteContents();
                                    context.invoke('editor.afterCommand');
                                }
                            }
                        }).render();
                    }
                });
                // summernote options-popover
                angular.extend($.summernote.options.popover, {
                    code: ['codeDialogShow', 'codeDelete']
                });
                // summernote modules
                angular.extend($.summernote.options.modules, {
                    linkDialog: function(context) {
                        context.memo('help.linkDialog.show', context.options.langInfo.help['linkDialog.show']);
                        //
                        this.initialize = function() {};
                        this.destroy = function() {};
                        //
                        this.show = function() {
                            var linkInfo = context.invoke('editor.getLinkInfo');
                            context.invoke('editor.saveRange');
                            _dialog.open().editLink(linkInfo).then(function(link) {
                                context.invoke('editor.restoreRange');
                                context.invoke('editor.createLink', link);
                            }, function() {
                                context.invoke('editor.restoreRange');
                            });
                        }
                    },
                    codeDialog: function(context) {
                        this.initialize = function() {};
                        this.destroy = function() {};
                        //
                        this.show = function() {
                            const range = context.invoke('editor.getLastRange');
                            const pre = $.summernote.dom.ancestor(range.sc, $.summernote.dom.isPre);
                            if (pre) {
                                context.invoke('editor.saveRange');
                                _dialog.open().editCode({
                                    lang: $(pre).find('code').attr('lang'),
                                    code: $(pre).text()
                                }).then(function(codeInfo) {
                                    var html = '';
                                    var lang = codeInfo.lang;
                                    var code = codeInfo.code;
                                    if (lang && hljs.getLanguage(lang)) {
                                        html = hljs.highlight(lang, code).value;
                                    } else {
                                        html = hljs.highlightAuto(code).value;
                                    }
                                    html = '<pre><code class="hljs" lang="' + lang + '">' + html + '</code></pre>';
                                    context.invoke('editor.restoreRange');
                                    var newRange = $.summernote.range.createFromNode(pre);
                                    newRange.insertNode($(html)[0]);
                                    context.invoke('editor.setLastRange', newRange.select());
                                    setTimeout(function() {
                                        context.invoke('editor.focus');
                                    }, 50);
                                }, function() {
                                    context.invoke('editor.restoreRange');
                                });
                            }
                        }
                    },
                    codePopover: function(context) {
                        var _this = this;
                        this.dom = $.summernote.dom;
                        this.events = {
                            'summernote.keyup summernote.mouseup summernote.change summernote.scroll': function(event) {
                                _this.update(event);
                            },
                            'summernote.disable summernote.dialog.shown summernote.blur': function() {
                                _this.hide();
                            }
                        };
                        //
                        this.initialize = function() {
                            _this.popover = $.summernote.ui.popover({
                                className: 'note-code-popover'
                            }).render().appendTo(context.options.container);
                            const content = _this.popover.find('.popover-content,.note-popover-content');

                            context.invoke('buttons.build', content, context.options.popover.code);

                            _this.popover.on('mousedown', function(e) { e.preventDefault(); });
                        };
                        this.destroy = function() {
                            _this.popover.remove();
                        };
                        //
                        this.update = function(event) {
                            if (!context.invoke('editor.hasFocus')) {
                                _this.hide();
                                return;
                            }
                            //
                            const range = context.invoke('editor.getLastRange');
                            if (range.isCollapsed() && _this.dom.isOnPre(range)) {
                                const pre = _this.dom.ancestor(range.sc, _this.dom.isPre);

                                const position = _this.dom.posFromPlaceholder(pre);
                                const containerOffset = $(context.options.container).offset();
                                position.top -= containerOffset.top + 10;
                                position.left -= containerOffset.left;

                                _this.popover.css({
                                    display: 'block',
                                    left: position.left,
                                    top: position.top,
                                });
                            } else {
                                _this.hide();
                            }
                        };
                        this.hide = function() {
                            _this.popover.hide();
                        }
                    }
                });

                return {
                    post: function(scope, element) {
                        var context = { init: true, reload: false };
                        // editor
                        var editor = element.find('>textarea');
                        editor.summernote({
                            lang: 'zh-CN',
                            focus: true,
                            height: null,
                            airMode: false,
                            toolbar: false,
                            statusbar: false,
                            shortcuts: true,
                            spellCheck: true,
                            codeviewFilter: true,
                            callbacks: {
                                onInit: function() {
                                    element.find('.note-editable').on('keyup mouseup', function() {
                                        var selection = $.summernote.range.createFromSelection();
                                        if (selection && selection.toString() != '') {
                                            scope.editor['cut'].enabled = true;
                                            scope.editor['copy'].enabled = true;
                                        } else {
                                            scope.editor['cut'].enabled = false;
                                            scope.editor['copy'].enabled = false;
                                        }
                                    });
                                },
                                onChange: function(contents) {
                                    $timeout(function() {
                                        if (!context.init && !context.reload) {
                                            scope.file.changed = true;
                                            scope.file.data = contents;
                                        }
                                        scope.editor['save'].enabled = scope.file.changed;
                                        scope.editor['undo'].enabled = editor.summernote('canUndo');
                                        scope.editor['redo'].enabled = editor.summernote('canRedo');
                                    });
                                    if (typeof(scope.onChanged) === 'function') {
                                        scope.onChanged();
                                    }
                                    //
                                    element.find('.note-editable').find('a, img, th, td')
                                        .unbind('mousedown')
                                        .bind('mousedown', function(event) {
                                            if (event.button == 2) {
                                                event.stopPropagation();
                                            }
                                        });
                                    element.find('.note-editable pre').attr('contenteditable', false);
                                },
                                onKeydown: function(event) {
                                    if (event.ctrlKey && event.keyCode == 83) {
                                        event.preventDefault();
                                        if (scope.file.changed) {
                                            scope.editor['save'].click();
                                        }
                                    }
                                }
                            }
                        });

                        scope.editor = {
                            'undo': scope.context.navbar['edit']['undo'],
                            'redo': scope.context.navbar['edit']['redo'],
                            //
                            'cut': scope.context.navbar['edit']['cut'],
                            'copy': scope.context.navbar['edit']['copy'],
                            'paste': scope.context.navbar['edit']['paste'],
                            //
                            'format': {
                                'bold': { click: function() {
                                    editor.summernote('bold');
                                }},
                                'italic': { click: function() {
                                    editor.summernote('italic');
                                }},
                                'underline': { click: function() {
                                    editor.summernote('underline');
                                }},
                                'strikethrough': { click: function() {
                                    editor.summernote('strikethrough');
                                }},
                                //
                                'justifyLeft': { click: function() {
                                    editor.summernote('justifyLeft');
                                }},
                                'justifyCenter': { click: function() {
                                    editor.summernote('justifyCenter');
                                }},
                                'justifyRight': { click: function() {
                                    editor.summernote('justifyRight');
                                }},
                                'justifyFull': { click: function() {
                                    editor.summernote('justifyFull');
                                }},
                                //
                                'indent': { click: function() {
                                    editor.summernote('indent');
                                }},
                                'outdent': { click: function() {
                                    editor.summernote('outdent');
                                }},
                                //
                                'superscript': { click: function() {
                                    editor.summernote('superscript');
                                }},
                                'subscript': { click: function() {
                                    editor.summernote('subscript');
                                }}
                            },
                            'clear': { enabled: true, click: function() {
                                editor.summernote('removeFormat');
                            }},
                            //
                            'save': scope.context.navbar['file']['save'],
                            //
                            'contextmenu': function(event, contextmenu) {
                                var target = (event.target || event.srcElement);
                                if ($(target).hasClass('note-control-selection-bg')) {
                                    contextmenu.removeClass('open');
                                } else {
                                    element.find('.note-popover').css('display', 'none');
                                    element.find('.note-control-selection').css('display', 'none');
                                }
                            }
                        };

                        scope.file.reloader = function(helper, reloaded) {
                            context.reload = true;
                            if (reloaded) {
                                loadData(scope.file.data);
                            } else if (scope.file.path) {
                                helper.read(scope.file).then(function(fileObj) {
                                    loadData(fileObj.data);
                                });
                            } else if (scope.file.data) {
                                loadData(scope.file.data);
                            } else {
                                context.init = false;
                                context.reload = false;
                            }

                            function loadData(data) {
                                editor.summernote('code', data);
                                editor.summernote('commit');
                                $timeout(function() {
                                    context.init = false;
                                    context.reload = false;
                                    scope.file.changed = false;
                                    scope.editor['save'].enabled = false;
                                }, 50);
                            }
                        }

                        scope.file.extractor = function() {
                            try {
                                scope.file.data = _prettier.format(editor.summernote('code'), {
                                    parser: 'html',
                                    printWidth: 500,
                                });
                            } catch (e) {
                                scope.file.data = editor.summernote('code');
                            }
                        }

                        scope.file.renderer = function(helper) {
                            // editor
                            editor.summernote('restoreRange');
                            editor.summernote('focus');
                            if (!scope.file.opened) {
                                scope.file.opened = true;
                                scope.file.reloader(helper);
                            }

                            // extendbar
                            extendbar.css('display', '');
                            toolscope.extendbar['fontColor'].click = function(forecolor, backcolor) {
                                if (forecolor) {
                                    editor.summernote('foreColor', forecolor);
                                }
                                editor.summernote('backColor', (backcolor || 'transparent'));
                            };
                            toolscope.extendbar['fontSize'].click = function(size) {
                                editor.summernote('fontSize', parseInt(size));
                            };
                            toolscope.extendbar['lineHeight'].click = function(height) {
                                editor.summernote('lineHeight', parseInt(height));
                            };
                            toolscope.extendbar['header'].click = function(level) {
                                editor.summernote('formatH' + level);
                                setTimeout(function() {
                                    editor.summernote('focus');
                                }, 50);
                            };
                            toolscope.extendbar['unorder'].click = function() {
                                editor.summernote('focus');
                                editor.summernote('insertUnorderedList');
                            };
                            toolscope.extendbar['order'].click = function() {
                                editor.summernote('focus');
                                editor.summernote('insertOrderedList');
                            };
                            toolscope.extendbar['link'].click = function(link) {
                                editor.summernote('restoreRange');
                                editor.summernote('createLink', {
                                    text: link.text, url: link.url, isNewWindow: true
                                });
                            };
                            toolscope.extendbar['image'].click = function(image) {
                                editor.summernote('restoreRange');
                                editor.summernote('insertImage', image.url, image.name);
                            };
                            toolscope.extendbar['table'].click = function(row, column) {
                                var html = '<table class="table table-bordered">';
                                for (var i = 1; i <= row; i++) {
                                    html += (i == 1 ? '<thead>' : (i == 2 ? '<tbody>' : ''));
                                    html += '</tr>';
                                    for (var j = 1; j <= column; j++) {
                                        html += (i == 1 ? '<th></th>' : '<td></td>');
                                    }
                                    html += '</tr>';
                                    html += (i == 1 ? '</thead>' : (i == row ? '</tbody>' : ''));
                                }
                                html += '</table>';
                                editor.summernote('restoreRange');
                                editor.summernote('insertNode', $(html)[0]);
                            };
                            toolscope.extendbar['source'].click = function(action, code, lang) {
                                editor.summernote('restoreRange');
                                var html = code;
                                if (action == 'html' || action == 'blockcode') {
                                    if (action == 'blockcode') {
                                        if (lang && hljs.getLanguage(lang)) {
                                            html = hljs.highlight(lang, code).value;
                                        } else {
                                            html = hljs.highlightAuto(code).value;
                                        }
                                        html = '<pre><code class="hljs" lang="' + lang + '">' + html + '</code></pre>';
                                    }
                                    editor.summernote('insertNode', $(html)[0]);
                                } else {
                                    if (action == 'blockquote') {
                                        html = '<blockquote><br></blockquote>';
                                    } else if (action == 'code') {
                                        html = '<code><br></code>';
                                    }
                                    var lastRange = editor.summernote('getLastRange');
                                    var newNode = lastRange.insertNode($(html)[0]);
                                    //var newRange = $.summernote.range.createFromNode(newNode);
                                    var newRange = $.summernote.range.create(newNode, 0).normalize();
                                    editor.summernote('setLastRange', newRange.select());
                                }
                                setTimeout(function(){
                                    editor.summernote('focus');
                                }, 50);
                            };

                            // navbar
                            scope.context.navbar['file']['save'].enabled = scope.file.changed;
                            scope.context.navbar['file']['save'].click = function() {
                                helper.save(scope.file);
                            };
                            scope.context.navbar['edit']['undo'].enabled = editor.summernote('canUndo');
                            scope.context.navbar['edit']['undo'].click = function() {
                                editor.summernote('restoreRange');
                                editor.summernote('undo');
                            };
                            scope.context.navbar['edit']['redo'].enabled = editor.summernote('canRedo');
                            scope.context.navbar['edit']['redo'].click = function() {
                                editor.summernote('restoreRange');
                                editor.summernote('redo');
                            };
                            var hasSelected = !_.isEmpty(($.summernote.range.createFromSelection() || '').toString());
                            scope.context.navbar['edit']['cut'].enabled = hasSelected;
                            scope.context.navbar['edit']['cut'].click = function() {
                                var selection = $.summernote.range.createFromSelection();
                                if (selection) {
                                    _util.copy(selection.toString());
                                    selection.deleteContents();
                                }
                            };
                            scope.context.navbar['edit']['copy'].enabled = hasSelected;
                            scope.context.navbar['edit']['copy'].click = function() {
                                var selection = $.summernote.range.createFromSelection();
                                if (selection) {
                                    _util.copy(selection.toString());
                                }
                            };
                            scope.context.navbar['edit']['paste'].click = function() {
                                // TODO
                            };
                            scope.context.navbar['edit']['selectAll'].enabled = true;
                            scope.context.navbar['edit']['selectAll'].click = function() {
                                editor.focus();
                                editor.selectAll();
                            }
                            scope.context.navbar['edit']['delete'].enabled = true;
                            scope.context.navbar['edit']['delete'].click = function() {
                                var selection = $.summernote.range.createFromSelection();
                                if (selection) {
                                    selection.deleteContents();
                                }
                            };
                            scope.context.navbar['edit']['find'].enabled = false;
                            scope.context.navbar['edit']['format'].enabled = false;

                            // statusbar
                            scope.context.statusbar.link = scope.file.path;
                            scope.context.statusbar.statuses = ['可写', '插入'];
                        }

                        // destroy
                        scope.$on('$destroy', function() {
                            editor.summernote('destroy');
                        });
                    }
                };
            }
        });
    }
});
